package net.wicp.tams.common.spring.quartz;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Set;

import javax.annotation.PostConstruct;

import org.quartz.CronScheduleBuilder;
import org.quartz.CronTrigger;
import org.quartz.Job;
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.JobExecutionContext;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.quartz.TriggerKey;
import org.quartz.impl.matchers.GroupMatcher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;

import lombok.extern.slf4j.Slf4j;
import net.wicp.tams.common.Conf;
import net.wicp.tams.common.Result;
import net.wicp.tams.common.apiext.StringUtil;
import net.wicp.tams.common.constant.dic.YesOrNo;
import net.wicp.tams.common.spring.quartz.bean.ScheduleJob;
import net.wicp.tams.common.spring.quartz.exe.IJob;
import net.wicp.tams.common.spring.quartz.exe.QuartzJobFull;
import net.wicp.tams.common.spring.quartz.exe.QuartzJobLess;

/**
 * 计划任务管理
 * 
 * @author zhoujunhui
 *
 */
@Configuration
@ConditionalOnBean(value = IScheduleJobStore.class)
@ConditionalOnProperty(name = "common.spring.quartz.enable", havingValue = "true")
@Slf4j
public class JobTaskService {
	@Autowired
	private SchedulerFactoryBean schedulerFactoryBean;
	@Autowired
	private IScheduleJobStore scheduleJobMapper;
	@Autowired
	private ApplicationContext applicationContext;

	public static String poNameInJobDataMap = "scheduleJob";

	/****
	 * 从数据库中取 区别于getAllJob
	 * 
	 * @return 得到数据库中所有的job
	 */
	public List<ScheduleJob> getAllTask() {
		return scheduleJobMapper.getAll(null);
	}

	/**
	 * 添加到数据库中 区别于addJob
	 * 
	 * @param job
	 *            要添加到数据库的job信息
	 * @return 添加结果
	 */
	public Result addTask(ScheduleJob job) {
		try {
			addJob(job);
			job.setCreateTime(new Date());
			scheduleJobMapper.insert(job);
			return Result.getSuc();
		} catch (SchedulerException e) {
			log.error("添加job失败", e);
			return Result.getError(e.getMessage());
		}
	}

	/***
	 * 从数据库中查询job
	 * 
	 * @param jobId
	 *            数据库的job主键
	 * @return job信息
	 */
	public ScheduleJob getTaskById(Long jobId) {
		return scheduleJobMapper.selectByPrimaryKey(jobId);
	}

	// 从数据库中查询job
	public ScheduleJob getTaskByGroupAndName(String jobGroup, String jobName) {
		return scheduleJobMapper.selectByGroupAndName(jobGroup, jobName);
	}

	// 更改任务状态
	public void changeStatus(Long jobId, String cmd) throws SchedulerException {
		ScheduleJob job = getTaskById(jobId);
		if (job == null) {
			return;
		}
		if ("stop".equals(cmd)) {
			deleteJob(job);
			job.setIsActiv(YesOrNo.no);
		} else if ("start".equals(cmd)) {
			job.setIsActiv(YesOrNo.yes);
			addJob(job);
		}
		scheduleJobMapper.updateByPrimaryKey(job);
	}

	/***
	 * 更改任务 cron表达式
	 * 
	 * @param jobId
	 *            数据库主键
	 * @param cron
	 *            执行表达式
	 * @throws SchedulerException
	 *             更新正在运行的job异常
	 */
	public void updateCron(Long jobId, String cron) throws SchedulerException {
		if (!checkEnable()) {
			return;
		}
		ScheduleJob job = getTaskById(jobId);
		if (job == null) {
			return;
		}
		job.setCronExpression(cron);
		if (job.getIsActiv() == YesOrNo.yes) {
			updateJobCron(job);
		}
		scheduleJobMapper.updateByPrimaryKey(job);

	}

	/**
	 * 添加任务
	 * 
	 * @param job
	 *            加到为执行任务信息
	 * @throws SchedulerException
	 *             添加时异常
	 */
	public void addJob(ScheduleJob job) throws SchedulerException {
		if (!checkEnable()) {
			return;
		}
		if (job == null || YesOrNo.yes != job.getIsActiv()) {
			return;
		}
		Scheduler scheduler = schedulerFactoryBean.getScheduler();
		log.debug(scheduler
				+ ".......................................................................................add");
		TriggerKey triggerKey = TriggerKey.triggerKey(job.getJobName(), job.getJobGroup());
		CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
		// 不存在，创建一个
		if (null == trigger) {
			Class<? extends Job> clazz = YesOrNo.yes == job.getIsConcurrent() ? QuartzJobLess.class
					: QuartzJobFull.class;
			JobDetail jobDetail = JobBuilder.newJob(clazz).withIdentity(job.getJobName(), job.getJobGroup()).build();
			jobDetail.getJobDataMap().put(poNameInJobDataMap, job);
			CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(job.getCronExpression());
			trigger = TriggerBuilder.newTrigger().withIdentity(job.getJobName(), job.getJobGroup())
					.withSchedule(scheduleBuilder).build();
			scheduler.scheduleJob(jobDetail, trigger);
		} else {
			// Trigger已存在，那么更新相应的定时设置
			CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(job.getCronExpression());
			// 按新的cronExpression表达式重新构建trigger
			trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();
			// 按新的trigger重新设置job执行
			scheduler.rescheduleJob(triggerKey, trigger);
		}
	}

	private boolean checkEnable() {
		if (StringUtil.isNotNull(Conf.get("common.spring.quartz.enable"))
				&& "true".equals(Conf.get("common.spring.quartz.enable"))) {
			return true;
		} else {
			return false;
		}
	}

	@PostConstruct
	public void init() throws Exception {
		// 这里获取任务信息数据
		List<ScheduleJob> jobList = scheduleJobMapper.getAll(YesOrNo.yes);
		for (ScheduleJob job : jobList) {
			addJob(job);
		}
	}

	// 获取所有计划中的任务列表
	public List<ScheduleJob> getAllJob() throws SchedulerException {
		Scheduler scheduler = schedulerFactoryBean.getScheduler();
		GroupMatcher<JobKey> matcher = GroupMatcher.anyJobGroup();
		Set<JobKey> jobKeys = scheduler.getJobKeys(matcher);
		List<ScheduleJob> jobList = new ArrayList<ScheduleJob>();
		for (JobKey jobKey : jobKeys) {
			List<? extends Trigger> triggers = scheduler.getTriggersOfJob(jobKey);
			for (Trigger trigger : triggers) {
				ScheduleJob job = new ScheduleJob();
				job.setJobName(jobKey.getName());
				job.setJobGroup(jobKey.getGroup());
				job.setDescription("触发器:" + trigger.getKey());
				Trigger.TriggerState triggerState = scheduler.getTriggerState(trigger.getKey());
				job.setJobStatus(triggerState);
				if (trigger instanceof CronTrigger) {
					CronTrigger cronTrigger = (CronTrigger) trigger;
					String cronExpression = cronTrigger.getCronExpression();
					job.setCronExpression(cronExpression);
				}
				jobList.add(job);
			}
		}
		return jobList;
	}

	// 所有正在运行的job
	public List<ScheduleJob> getRunningJob() throws SchedulerException {
		Scheduler scheduler = schedulerFactoryBean.getScheduler();
		List<JobExecutionContext> executingJobs = scheduler.getCurrentlyExecutingJobs();
		List<ScheduleJob> jobList = new ArrayList<ScheduleJob>(executingJobs.size());
		for (JobExecutionContext executingJob : executingJobs) {
			ScheduleJob job = new ScheduleJob();
			JobDetail jobDetail = executingJob.getJobDetail();
			JobKey jobKey = jobDetail.getKey();
			Trigger trigger = executingJob.getTrigger();
			job.setJobName(jobKey.getName());
			job.setJobGroup(jobKey.getGroup());
			job.setDescription("触发器:" + trigger.getKey());
			Trigger.TriggerState triggerState = scheduler.getTriggerState(trigger.getKey());
			job.setJobStatus(triggerState);
			if (trigger instanceof CronTrigger) {
				CronTrigger cronTrigger = (CronTrigger) trigger;
				String cronExpression = cronTrigger.getCronExpression();
				job.setCronExpression(cronExpression);
			}
			jobList.add(job);
		}
		return jobList;
	}

	// 暂停一个job
	public void pauseJob(ScheduleJob scheduleJob) throws SchedulerException {
		Scheduler scheduler = schedulerFactoryBean.getScheduler();
		JobKey jobKey = JobKey.jobKey(scheduleJob.getJobName(), scheduleJob.getJobGroup());
		scheduler.pauseJob(jobKey);
	}

	// 恢复一个job
	public void resumeJob(ScheduleJob scheduleJob) throws SchedulerException {
		Scheduler scheduler = schedulerFactoryBean.getScheduler();
		JobKey jobKey = JobKey.jobKey(scheduleJob.getJobName(), scheduleJob.getJobGroup());
		scheduler.resumeJob(jobKey);
	}

	// 删除一个job,不删除数据库
	public void deleteJob(ScheduleJob scheduleJob) throws SchedulerException {
		if (!checkEnable()) {
			return;
		}
		Scheduler scheduler = schedulerFactoryBean.getScheduler();
		JobKey jobKey = JobKey.jobKey(scheduleJob.getJobName(), scheduleJob.getJobGroup());
		scheduler.deleteJob(jobKey);
	}

	public void deleteJobDb(Long jobId) {
		scheduleJobMapper.deleteByPrimaryKey(jobId);
	}

	// 立即执行job
	public void runAJobNow(ScheduleJob scheduleJob) throws SchedulerException {
		Scheduler scheduler = schedulerFactoryBean.getScheduler();
		JobKey jobKey = JobKey.jobKey(scheduleJob.getJobName(), scheduleJob.getJobGroup());
		scheduler.triggerJob(jobKey);
	}

	// 更新job时间表达式
	public void updateJobCron(ScheduleJob scheduleJob) throws SchedulerException {
		if (!checkEnable()) {
			return;
		}
		Scheduler scheduler = schedulerFactoryBean.getScheduler();
		TriggerKey triggerKey = TriggerKey.triggerKey(scheduleJob.getJobName(), scheduleJob.getJobGroup());
		CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
		CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(scheduleJob.getCronExpression());
		trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();
		scheduler.rescheduleJob(triggerKey, trigger);
	}

	public void callBusiExe(ScheduleJob scheduleJob) {
		// applicationContext
		IJob job = null;
		if (StringUtil.isNotNull(scheduleJob.getSpringName())) {
			job = (IJob) applicationContext.getBean(scheduleJob.getSpringName());
		} else {
			try {
				Class<?> clazz = Class.forName(scheduleJob.getBeanClass());
				job = (IJob) clazz.newInstance();
			} catch (Exception e) {
				log.error("得到类[" + scheduleJob.getBeanClass() + "]错误.", e);
			}
		}
		if (job != null) {
			job.exe();
		}
	}

}
